package com.weilan.ark3.controller;


import com.weilan.ark3.buffer.DataArray;
import com.weilan.ark3.buffer.DataArray2;
import com.weilan.ark3.buffer.MsgQueue;
import com.weilan.ark3.common.Communication;
import com.weilan.ark3.common.GlobalParam;
import com.weilan.ark3.common.Result;
import com.weilan.ark3.entity.*;
import com.weilan.ark3.sqlite.SQLiteConfigUtils;
import com.weilan.ark3.sqlite.SQLiteParamUtils;
import com.weilan.ark3.sqlite.SQLitePeakUtils;
import com.weilan.ark3.tcp.TcpMaster;
import com.weilan.ark3.tcp.TcpSlave;
import com.weilan.ark3.utils.ArrayUtils;
import com.weilan.ark3.utils.DateUtils;
import com.weilan.ark3.utils.HexUtils;
import com.weilan.ark3.utils.ammeterUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import static com.weilan.ark3.buffer.OverallSituation.*;

@RestController
@CrossOrigin
@RequestMapping("weilan/system/fenggu")
public class PeakValleyController {


    static int b = 0;
    static ControlDto controlDto1 = new ControlDto();
    static ControlDto start = new ControlDto();

    static Logger test = LoggerFactory.getLogger("HTTPServer");

    //峰谷控制(不带PUC,单台柜子)
    public static void fengguCtl() {
        PvCtlDto pvCtlDto = new PvCtlDto();
        //获取系统模式(单台柜子，测试使用，存在缺陷，当两台柜子时，第一台故障无法获得系统的运行模式)
        //获取数据库中的配置参数
        ControlDto controlDto2 = new ControlDto();
        OverallEMSData = DataArray.slaveDataMap.get(1);
        ParamDto param = SQLiteParamUtils.query();
        int soc_h = Integer.parseInt(param.getSoc_h());
        int soc_l = Integer.parseInt(param.getSoc_l());
        int stepSize = param.getStep_size();
        int deviceNum = param.getDeviceNum();//设备数量
        int allowance = param.getGrid_alw() * 10;//容差
        int p_max = Integer.parseInt(param.getP_max()) * 10;//场站最大功率
        int anti_reflux = param.getAnti_reflux();//防逆流的标志
        int bcmSocl = param.getBcm_socl();//预留SOC
        int expansionMark = param.getExpansion_mark();

        short meter_com_status = Communication.ammeter_interrupt;//电表通讯状态 0 表示正常 1 表示中断
        short sys_state = OverallEMSData[1]; // 1:停止 2：运行 3：故障
        short sys_ctl_status = OverallEMSData[61];//系统控制状态
        short sys_ctl_power = OverallEMSData[63];//当前系统功率
        short Grid_meter_power = ammeterUtils.getGrid_Power();

        //允许充电
        pvCtlDto.setAllow_charge(OverallEMSData[5]);
        //允许放电
        pvCtlDto.setAllow_discharge(OverallEMSData[4]);
        //电网功率
        pvCtlDto.setGrid_power(Grid_meter_power);
        //高压保护
        pvCtlDto.setH_alarm(OverallEMSData[16]);
        //低压保护
        pvCtlDto.setL_alarm(OverallEMSData[17]);
        //光伏现在功率
        pvCtlDto.setPv_power(OverallEMSData[368]);
        //系统soc
        pvCtlDto.setSys_soc(OverallEMSData[26]);
        //系统指令功率
        pvCtlDto.setSys_cmd(OverallEMSData[63]);
        //容差
        pvCtlDto.setAllow_range(allowance);
        //soc_h
        pvCtlDto.setSoc_h(soc_h);
        //soc_l
        pvCtlDto.setSoc_l(soc_l);
        //步长
        pvCtlDto.setStep_size(stepSize);
        //BCM_SOCL
        pvCtlDto.setBcm_socl(bcmSocl);
        //判断是否为并网模式


        if (meter_com_status == 0 && sys_state != 3) {
            //排除0功率保护
            if ((sys_state == 2 && sys_ctl_status == 6) || (sys_state == 1 && sys_ctl_status == 6)) {
                System.out.println("----!!!!!!--"+GlobalParam.fengguZero);
                //用于第一次降功率
                if (GlobalParam.fengguZero == 0) {
                    start.setSlaveId(255);
                    start.setAddress(302);
                    start.setValue(0);
                    MsgQueue.writeToControlTcp(start);
                    GlobalParam.fengguZero = 1;
                    GlobalParam.backZero = 0;
                    GlobalParam.pvZero = 0;
                    GlobalParam.bansign = 0;
                    System.out.println("---------"+GlobalParam.fengguZero);
                }
                //1.判断当前是否存在时段
                //获得所有设置的时段
                //先发启动
                //时段校验算出功率
                AtomicInteger AtoPower_ESS = PV_Time_ctl(pvCtlDto);
                int Power_Time = AtoPower_ESS.intValue();
                if ((pvCtlDto.getSys_soc() <= pvCtlDto.getSoc_l() || pvCtlDto.getL_alarm() != 0) && Power_Time > 0) {
                    if (pvCtlDto.getSys_cmd() > 0) {
                        controlDto2.setSlaveId(255);
                        controlDto2.setAddress(302);
                        controlDto2.setValue(0);
                        MsgQueue.writeToControlTcp(controlDto2);
                    }
                } else if ((pvCtlDto.getSys_soc() >= pvCtlDto.getSoc_h() || pvCtlDto.getH_alarm() != 0) && Power_Time < 0) {
                    if (pvCtlDto.getSys_cmd() < 0) {
                        controlDto2.setSlaveId(255);
                        controlDto2.setAddress(302);
                        controlDto2.setValue(0);
                        MsgQueue.writeToControlTcp(controlDto2);
                    }
                }
                if (b > 5) {
                    start.setSlaveId(255);
                    start.setAddress(301);
                    start.setValue(6);
                    MsgQueue.writeToControlTcp(start);
                    b = 0;
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                } else {
                    b++;
                }
                List<FengguDto> query = SQLitePeakUtils.query();
                if (query.size() > 0) {
//                    query.forEach(item ->
//                            {
                    for (int j = 0; j < query.size(); j++) {
                        FengguDto item = query.get(j);
                        String tiemNow = DateUtils.tiemHour();
                        String startTime = item.getStartTime();
                        String endTime = item.getEndTime();
                        int mode = item.getMode();//1 表示峰谷  0 表示扩容
                        int time_power = item.getPower() * 10;//时段功率
                        boolean result = DateUtils.belongCalendar(tiemNow, startTime, endTime);
                        int runState = item.getRunState();//充放状态
                        if (result) {
                            //判断时段为充电还是放电  0：空闲 1：充 2：放
                            if (runState == 0) {
                                //给系统下发0功率
                                start.setSlaveId(255);
                                start.setAddress(302);
                                start.setValue(0);
                                MsgQueue.writeToControlTcp(start);
                            } else if (runState == 1) {
                                //充电时
                                int p1 = (p_max - allowance) - Grid_meter_power - sys_ctl_power;
                                int i = Math.min(-p1, 0);
                                int p2 = Math.max(i, time_power);
                                int p3 = Math.max(p2, -pvCtlDto.getAllow_charge());
                                powerAllocation(deviceNum, pvCtlDto.getAllow_charge(), p3, Grid_meter_power);
                            } else if (runState == 2) {
                                //判断SOC <= BCM_SOCL && 扩容标志为1
                                if ((pvCtlDto.getSys_soc() > pvCtlDto.getBcm_socl() || expansionMark == 0) && mode == 1) {

                                    test.info("峰谷" + "soc" + pvCtlDto.getSys_soc() + "bcm" + pvCtlDto.getBcm_socl() + "expansionMark" + expansionMark + "mode" + mode);
                                    //放电时需要考虑是否防逆流
                                    //判断防逆流是否打开（ 0：不防逆流 1：防逆流）
                                    if (anti_reflux == 1) {
                                        //计算需要下发的功率
                                        int p1 = Grid_meter_power + sys_ctl_power - allowance;
                                        //如果计算出P1小于0则直接取0
                                        int i = Math.max(p1, 0);
                                        int p2 = Math.min(i, time_power);
                                        int p3 = Math.min(p2, pvCtlDto.getAllow_discharge());
                                        //下发功率(需要进行功率分配，)----无PUC时
                                        powerAllocation(deviceNum, pvCtlDto.getAllow_discharge(), p3, Grid_meter_power);
                                    } else {
                                        //计算负载功率
                                        int p_load = sys_ctl_power + Grid_meter_power;
                                        int p1 = (p_max - allowance) + p_load;
                                        int i = Math.max(p1, 0);
                                        int p2 = Math.min(time_power, i);
                                        int p3 = Math.min(p2, pvCtlDto.getAllow_discharge());
                                        //下发功率(需要进行功率分配，)----无PUC时
                                        powerAllocation(deviceNum, pvCtlDto.getAllow_discharge(), p3, Grid_meter_power);
                                    }
                                } else {
                                    test.info("扩容");
                                    //扩容模式
                                    int P1 = Grid_meter_power - p_max + sys_ctl_power + allowance;
                                    int i = Math.max(P1, 0);
                                    int P2 = Math.min(i, time_power);
                                    int P3 = Math.min(P2, pvCtlDto.getAllow_discharge());
                                    test.info("P3功率为" + P3);
                                    powerAllocation(deviceNum, pvCtlDto.getAllow_discharge(), P3, Grid_meter_power);
                                }
                            }
                            GlobalParam.a = 0;
                        } else {
                            GlobalParam.a++;
                            if (GlobalParam.a == query.size()) {
                                start.setSlaveId(255);
                                start.setAddress(GlobalParam.power);
                                start.setValue(GlobalParam.zeroP);
                                MsgQueue.writeToControlTcp(start);
                                //下发之后0功率以后，需要重置计数
                                GlobalParam.a = 0;
                            }
                        }
                    }
//                            }
//                    );
                }
            } else {
                //系统停止，下发启动指令（不带PUC，因此下发255）
                start.setSlaveId(255);
                start.setAddress(301);
                start.setValue(6);
                MsgQueue.writeToControlTcp(start);

            }
        } else {
            //下发停止指令（不带PUC，因此下发255）
            start.setSlaveId(255);
            start.setAddress(301);
            start.setValue(2);
            MsgQueue.writeToControlTcp(start);
            //System.out.println(" id：255，下发地址：301 value: 2" + "电网功率" + Grid_meter_power);
        }
        System.out.println("結束"+GlobalParam.fengguZero);
    }

    /*
     * Way  :功率分配算法
     * param: deviceNum 设备数量  volume 系统容量  total 下发的总功率
     * */
    public static void powerAllocation(int deviceNum, int volume, int total, int Grid_meter_power) {
        int minPower = 0;
        //系统容量作为分母
        if (volume != 0 && deviceNum != 0 && total != 0) {
            int i = ArrayUtils.NormalNum(total);
            minPower = ArrayUtils.MinPower(total, i);
        }
        controlDto1.setSlaveId(255);
        controlDto1.setAddress(302);
        controlDto1.setValue(minPower);
        MsgQueue.writeToControlTcp(controlDto1);
        updateData(deviceNum, 302, minPower);
    }

    public static void updateData(int num, int addr, int val) {
        for (int i = tcpSlaveStartId; i < tcpSlaveStartId + num; i++) {
            int flag = (OverallEMSData[28] >> i) & 1;
            if (flag == 0) {
                short[] shorts1 = DataArray.slaveDataMap.get(i);
                shorts1[addr] = (short) val;
                TcpSlave.slaveMap.get(i).setHoldingRegister(addr, (short) val);
            }

        }
    }

    //时段功率校验
    public static AtomicInteger PV_Time_ctl(PvCtlDto pvCtlDto) {
        AtomicInteger Powe_ESS = new AtomicInteger();
        //获得所有设置的时段
        List<FengguDto> query = SQLitePeakUtils.query();
        if (query.size() > 0) {
            GlobalParam.a = 0;
            for (int i = 0; i < query.size(); i++) {
                FengguDto item = query.get(i);
                String tiemNow = DateUtils.tiemHour();
                String startTime = item.getStartTime();
                String endTime = item.getEndTime();
                boolean result = DateUtils.belongCalendar(tiemNow, startTime, endTime);
                int Power = item.getPower();
                int timePower = Power * 10;
                int allowCharge = pvCtlDto.getAllow_charge();
                int allowDischarge = pvCtlDto.getAllow_discharge();
                if (result == true) {
                    if (timePower < 0) {
                        int min = Math.min(Math.abs(timePower), Math.abs(allowCharge));
                        Powe_ESS.set(-min);
                    } else if (timePower == 0) {
                        Powe_ESS.set(0);
                    } else if (timePower > 0) {

                        int min = Math.min(Math.abs(timePower), Math.abs(allowDischarge));
                        Powe_ESS.set(min);
                    }
                    GlobalParam.a = 0;
                    break;
                } else {
                    GlobalParam.a++;
                    if (GlobalParam.a == query.size()) {
                        Powe_ESS.set(0);
                    }
                }
            }
        }
        return Powe_ESS;
    }

    /*
     *峰谷时段
     **/
    @PostMapping("add")
    public Result<Object> creatFenggu(@RequestBody ArrayList<FengguDto> list) {
        if (list.size() > 0) {
            for (int i = 0; i < list.size(); i++) {
                FengguDto fengguDto = list.get(i);
                fengguDto.setId(i + 1);
                if (fengguDto.getRunState() == 1) {
                    fengguDto.setPower(-fengguDto.getPower());
                }
                SQLitePeakUtils.updateToDB(fengguDto);
            }
        }
        return Result.success(new ArrayList<>());
    }


    /*
     *
     * */
    @PostMapping("query")
    public List<FengguDto> queryDB() {
        return SQLitePeakUtils.query();
    }
}
